use crate::token::Token;

trait Node {}

// ---------------------------------
pub struct Ast {
   pub statements: Vec<AstStatement>,
}

impl Ast {
   pub fn new() -> Self {
      Self {
         statements: Vec::new(),
      }
   }

   pub fn add_statement(&mut self, statement: AstStatement) {
      self.statements.push(statement);
   }

   pub fn visit(&self, visitor: &mut dyn AstVisitor) {
      for statement in &self.statements {
         visitor.visit_statement(statement);
      }
   }

   pub fn visualize(&self) {
      let mut printer = AstPrinter { indent: 0 };
      self.visit(&mut printer);
   }
}

pub trait AstVisitor {
   fn do_visit_statement(&mut self, statement: &AstStatement) {
      match &statement.kind {
         AstStatementKind::Expression(expr) => {
            self.visit_expression(expr);
         }
      }
   }

   fn do_visit_expression(&mut self, expression: &AstExpression) {
      use AstExpressionKind::*;
      match &expression.kind {
         NumberInt(number) => {
            self.visit_number(number);
         }
         NumberFloat(number) => {
            self.visit_number(number);
         }
         Binary(expr) => {
            self.visit_binary_expression(expr);
         }
         Parenthesized(expr) => {
            self.visit_parenthesized_expression(expr);
         }
         Unary(expr) => self.visit_unary_expression(expr),
      };
   }

   fn visit_statement(&mut self, statement: &AstStatement) {
      self.do_visit_statement(statement);
   }

   fn visit_expression(&mut self, expression: &AstExpression) {
      self.do_visit_expression(expression);
   }

   fn visit_number(&mut self, number: &AstNumberExpression);

   fn visit_binary_expression(
      &mut self,
      binary_expression: &AstBinaryExpression,
   );

   fn visit_parenthesized_expression(
      &mut self,
      parenthesized_expression: &AstParenthesizedExpression,
   ) {
      self.visit_expression(&parenthesized_expression.expression);
   }

   fn visit_unary_expression(&mut self, unary_expression: &AstUnaryExpression);
}

pub struct AstPrinter {
   indent: usize,
}

impl AstPrinter {
   fn print_with_indent(&mut self, text: &str) {
      println!("{}{}", " ".repeat(self.indent), text);
   }
}
const LEVEL_TNDENT: usize = 2;

impl AstVisitor for AstPrinter {
   fn visit_statement(&mut self, statement: &AstStatement) {
      self.print_with_indent("Statement:");
      self.indent += LEVEL_TNDENT;
      AstVisitor::do_visit_statement(self, statement);
      self.indent -= LEVEL_TNDENT;
   }

   fn visit_number(&mut self, number: &AstNumberExpression) {
      self.print_with_indent(&format!("Number: {}", number.number));
   }

   fn visit_unary_expression(&mut self, unary_expression: &AstUnaryExpression) {
      todo!()
   }
   fn visit_binary_expression(
      &mut self,
      binary_expression: &AstBinaryExpression,
   ) {
      todo!()
   }
}
// ---------------------------------

// ---------------------------------
#[derive(Debug, Clone)]
pub enum AstStatementKind {
   Expression(AstExpression),
}
#[derive(Debug, Clone)]
pub struct AstStatement {
   kind: AstStatementKind,
}
impl AstStatement {
   pub fn new(kind: AstStatementKind) -> Self {
      Self { kind }
   }

   pub fn expression(expr: AstExpression) -> Self {
      Self::new(AstStatementKind::Expression(expr))
   }
}
// ---------------------------------

#[derive(Debug, Clone)]
pub struct AstNumberExpression {
   number: f64,
}

impl AstNumberExpression {
   pub fn new(number: f64) -> Self {
      Self { number }
   }

   pub fn get_number(&self) -> f64 {
      self.number
   }
}

#[derive(Debug, Clone)]
pub enum AstBinaryOperatorKind {
   Plus,
   Minus,
   Multiply,
   Divide,
   Power,
   Negative,
}

#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)]
pub enum OperatorPrecedence {
   Lowest,           // 0
   AddOrSubtract,    // 1
   MultiplyOrDivide, // 2
   Power,            // 3
   Negative,         // 4
}

#[derive(Debug, Clone)]
pub struct AstBinaryOperator {
  pub kind: AstBinaryOperatorKind,
   token: Token,
}

impl AstBinaryOperator {
   pub fn new(kind: AstBinaryOperatorKind, token: Token) -> Self {
      Self { kind, token }
   }

   pub fn precedence(&self) -> OperatorPrecedence {
      use AstBinaryOperatorKind::*;
      // 优先级
      match &self.kind {
         Plus | Minus => OperatorPrecedence::AddOrSubtract,
         Multiply | Divide => OperatorPrecedence::MultiplyOrDivide,
         Power => OperatorPrecedence::Power,
         Negative => OperatorPrecedence::Negative,
      }
   }
}

#[derive(Debug, Clone)]
pub struct AstBinaryExpression {
   pub left: Box<AstExpression>,
   pub operator: AstBinaryOperator,
   pub right: Box<AstExpression>,
}

#[derive(Debug, Clone)]
pub enum AstUnaryOperatorKind {
   Minus, // - 负号运算符
}

#[derive(Debug, Clone)]
pub struct AstUnaryOperator {
   pub kind: AstUnaryOperatorKind,
   pub token: Token,
}

impl AstUnaryOperator {
   pub fn new(kind: AstUnaryOperatorKind, token: Token) -> Self {
      Self { kind, token }
   }
}
#[derive(Debug, Clone)]
pub struct AstUnaryExpression {
   pub operator: AstUnaryOperator,  // 负号或非运算符
   pub operand: Box<AstExpression>, // 操作数
}

#[derive(Debug, Clone)]
pub struct AstParenthesizedExpression {
   pub expression: Box<AstExpression>,
}

// ---------------------------------
#[derive(Debug, Clone)]
pub enum AstExpressionKind {
   NumberInt(AstNumberExpression),            // 3
   NumberFloat(AstNumberExpression),          // 3.4
   Binary(AstBinaryExpression),               // 4 + 2 * 3 + 1
   Parenthesized(AstParenthesizedExpression), // (4 + 2) * 3 + 1
   Unary(AstUnaryExpression),                 // 一元运算符: - 负号，! 非
}
#[derive(Debug, Clone)]
pub struct AstExpression {
   kind: AstExpressionKind,
}
impl AstExpression {
   pub fn new(kind: AstExpressionKind) -> Self {
      Self { kind }
   }

   // 3
   pub fn number(kind: AstExpressionKind) -> Self {
      match kind {
         AstExpressionKind::NumberInt(number) => {
            Self::new(AstExpressionKind::NumberInt(number))
         }
         AstExpressionKind::NumberFloat(number) => {
            Self::new(AstExpressionKind::NumberFloat(number))
         }
         _ => panic!("xxxx"),
      }
   }

   // 4 + 2 * 3
   pub fn binary(
      operator: AstBinaryOperator,
      left: AstExpression,
      right: AstExpression,
   ) -> Self {
      AstExpression::new(AstExpressionKind::Binary(AstBinaryExpression {
         left: Box::new(left),
         operator,
         right: Box::new(right),
      }))
   }

   // (4 + 2) * 3
   pub fn parenthesized(expression: Self) -> Self {
      Self::new(AstExpressionKind::Parenthesized(
         AstParenthesizedExpression {
            expression: Box::new(expression),
         },
      ))
   }

   // -  !
   pub fn unary(operator: AstUnaryOperator, operand: AstExpression) -> Self {
      AstExpression::new(AstExpressionKind::Unary(AstUnaryExpression {
         operator,
         operand: Box::new(operand),
      }))
   }
}
// ---------------------------------
